﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mahanet.Abyss.PathCalculation
{
	public class Plane3D
	{
		#region Fields

		private Coordinate3D m_PlanePoint;

		#endregion

		#region Public Properties

		public Vector3D Normal { get; private set; }

		#endregion

		#region C'tor

		public Plane3D(Vector3D normal, Coordinate3D planePoint)
		{
			if (normal.IsZero())
			{
				throw new ArgumentException("Normal cannot be empty vector", "normal");
			}

			this.Normal = normal.Normalized();
			this.m_PlanePoint = planePoint;
		}

		#endregion

		#region Public Methods

		public double OrientedDistance(Coordinate3D point)
		{
			Vector3D pointToPlane = point - m_PlanePoint;
			double projectedToNormal = pointToPlane.Dot(this.Normal);
			double distance = projectedToNormal / this.Normal.Length();

			return distance;
		}

		public bool IsPointOnPlane(Coordinate3D point)
		{
			Vector3D pointToPlane = point - m_PlanePoint;
			double projectedToNormal = pointToPlane.Dot(this.Normal);

			return PrecisionUtils.IsZero(projectedToNormal);
		}

		public double AngleTo(Vector3D fireVector)
		{
			// TODO test
			return (Math.PI / 2) - fireVector.AngleTo(Normal);
		}

		public Coordinate3D GetIntersectionPoint(Line3D line)
		{
			double lineNormalDot = line.NormalizedDirection.Dot(Normal);
			if (PrecisionUtils.IsZero(lineNormalDot))
			{
				// The line and plane are parallel
				return IsPointOnPlane(line.LinePoint) ? line.LinePoint : null;
			}

			Vector3D originalToPlaneVector = new Vector3D(line.LinePoint, m_PlanePoint);

			double projectionToLineDirection = originalToPlaneVector.Dot(line.NormalizedDirection);

			Vector3D projectedVector = projectionToLineDirection * line.NormalizedDirection;

			Coordinate3D intersectionPoint = line.LinePoint + projectedVector;

			return intersectionPoint;
		}

		public override bool Equals(object obj)
		{
			if (obj == null || GetType() != obj.GetType())
			{
				return false;
			}

			Plane3D other = obj as Plane3D;

			Vector3D cross = this.Normal.Cross(other.Normal);
			if (!cross.IsZero())
			{
				return false;
			}

			return IsPointOnPlane(other.m_PlanePoint);
		}

		public override int GetHashCode()
		{
			return this.Normal.GetHashCode();
		}

		#endregion
	}
}
